<?php

/**
 * This file is part of the Codizer package.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright   Copyright (c) 2010 Advisa (http://www.advisa.fr)
 * @author      Codizer Core Team <team@codizer.org>
 * @package     Codizer
 */

class f_relation_Manager
{
	private static $instance;
	
	/**
	 * @return f_relation_Manager
	 */
	private function __construct()
	{
		return $this;
	}
	
	/**
	 * Get singleton instance
	 *
	 * @return f_relation_Manager
	 */
	public static function getInstance()
	{
		if(self::$instance === null)
		{
			self::$instance = new self();
		}
		return self::$instance;
	}
	
// ======================================== SETTERS	
	
	/**
	 * Add/update a relation in f_relation by document ids
	 *
	 * @param integer $parentId
	 * @param integer $childId
	 * @param string $relationType
	 * @param integer $orderNumber
	 * @param string $lang
	 * @return unknown
	 */
	public function setRelationByIds($parentId, $childId, $relationType, $orderNumber = null, $lang = null)
	{
		$documentProvider = f_document_Provider::getInstance();
		$parentDoc = $documentProvider->getByUniqueId($parentId);
		$childDoc = $documentProvider->getByUniqueId($childId);
		return $this->setRelation($parentDoc, $childDoc, $relationType, $orderNumber, $lang);
	}
	
	/**
	 * Add/update a relation in f_relation
	 *
	 * @param f_document_BaseDocument $parentDocument
	 * @param f_document_BaseDocument $childDocument
	 * @param string $relationType
	 * @param integer $orderNumber
	 * @param string $lang
	 */
	public function setRelation($parentDocument, $childDocument, $relationType, $orderNumber = null, $lang = null)
	{
		$dbProvider = f_database_Provider::getInstance();

		if(is_null($lang))
		{
			$context = f_core_Context::getInstance();
			$lang = $context->getLang();
		}
		
		if(is_null($orderNumber))
		{
			$lastNumber = $this->getLastOrderNumber($parentDocument, $relationType);
			$orderNumber = $lastNumber + 1;
		}
        else
        {
            $childIds = $this->getChildIds($parentDocument->getUniqueId(), "*", $lang);
            $orderingArray = array();
            for($i=0; $i<count($childIds); $i++)
            {
                if($i >= $orderNumber)
                {
                    $orderingArray[$i+1] = $childIds[$i]['child_id'];
                }
                else
                {
                    $orderingArray[$i] = $childIds[$i]['child_id'];
                }
            }
            $this->orderRelations($parentDocument->getUniqueId(), $lang, $orderingArray);
        }
		
		$params = array();				
		
		$params['parent_id'] = $parentDocument->getUniqueId();
		$params['child_id'] = $childDocument->getUniqueId();
		$params['relation_type'] = $relationType;
		$params['parent_model'] = $parentDocument->getModel();
		$params['child_model'] = $childDocument->getModel();
		$params['relation_order'] = $orderNumber;
		$params['lang'] = $lang;		
		
		// If the relation does not exist in f_relation...
		if(!$this->checkIfRelationExists($parentDocument, $childDocument, $relationType, $lang))
		{
			// and documents are not DELETED...
			if($parentDocument->getPublicationstatus() != "DELETED" && $childDocument->getPublicationstatus() != "DELETED")
			{			
				$dbProvider->insert("f_relation", $params);
			}		
		}
		// else if relation exists...
		else
		{			
			// and one document is deleted...
			if($parentDocument->getPublicationstatus() == "DELETED" || $childDocument->getPublicationstatus() == "DELETED")
			{					
				// clear relation...
				$this->clearRelation($parentDocument, $childDocument, $relationType, $lang);
			}
			else
			{
				// Update
				$conditions['parent_id'] = $params['parent_id'];
				$conditions['child_id'] = $params['child_id'];
				$conditions['relation_type'] = $params['relation_type'];
				$conditions['lang'] = $params['lang'];				
				
				$dbProvider->updateByKey("f_relation", $conditions ,$params);
			}
		}
	}
	
	/**
	 * Retrieve the last order number
	 *
	 * @param f_document_BaseDocument $parentDocument
	 * @param string $relationType
	 * @return integer
	 */
	public function getLastOrderNumber($parentDocument, $relationType)
	{
		$dbProvider = f_database_Provider::getInstance();
		
		$sql = "SELECT `relation_order` FROM `f_relation` WHERE `parent_id`=:parentId AND `relation_type`=:relationType ORDER BY `relation_order` DESC LIMIT 1";
		
		$orderNumber = $dbProvider->setQuery($sql)->execute(array("parentId" => $parentDocument->getUniqueId(), "relationType" => $relationType))->getQueryResult(PDO::FETCH_COLUMN);
		
		return $orderNumber;
	}

    public function getNextDocument($parentDocument, $childDocument, $relationType)
    {
        $dbProvider = f_database_Provider::getInstance();

        $sql = "SELECT `child_id`, `child_model` FROM `f_relation` WHERE `parent_id`=:parentId AND `relation_type`=:relationType AND `lang`=:lang AND `relation_order`> (SELECT `relation_order` FROM `f_relation` WHERE `child_id`=:childId LIMIT 1) ORDER BY relation_order ASC LIMIT 1";

        $result = $dbProvider->setQuery($sql)->execute(array(
                'parentId' => $parentDocument->getUniqueId(),
                'relationType' => $relationType,
                'lang' => $parentDocument->getLang(),
                'childId' => $childDocument->getUniqueId()))
                ->getQueryResult(PDO::FETCH_ASSOC);
        
        if(count($result) > 0)
        {
            return f_document_Provider::getInstance()->getByModelAndUniqueId($result['child_model'], $result['child_id'], $parentDocument->getLang());
        }

        return null;
    }

    public function getPreviousDocument($parentDocument, $childDocument, $relationType)
    {
        $dbProvider = f_database_Provider::getInstance();

        $sql = "SELECT `child_id`, `child_model` FROM `f_relation` WHERE `parent_id`=:parentId AND `relation_type`=:relationType AND `lang`=:lang AND `relation_order`< (SELECT `relation_order` FROM `f_relation` WHERE `child_id`=:childId LIMIT 1) ORDER BY relation_order DESC LIMIT 1";

        $result = $dbProvider->setQuery($sql)->execute(array(
                'parentId' => $parentDocument->getUniqueId(),
                'relationType' => $relationType,
                'lang' => $parentDocument->getLang(),
                'childId' => $childDocument->getUniqueId()))
                ->getQueryResult(PDO::FETCH_ASSOC);

        if(count($result) > 0)
        {
            return f_document_Provider::getInstance()->getByModelAndUniqueId($result['child_model'], $result['child_id'], $parentDocument->getLang());
        }

        return null;
    }

    /**
     * Retriever the order number of the relation
     *
     * @param document $parentDocument
     * @param document $childDocument
     * @param string $relationType
     * @param string $lang
     * @return string
     */
    public function getOrderNumber($parentDocument, $childDocument, $relationType, $lang)
    {
        $dbProvider = f_database_Provider::getInstance();

		$sql = "SELECT relation_order FROM f_relation WHERE parent_id=:parentid AND child_id=:childid AND relation_type=:relationType AND lang=:lang";

		$results = array();

		$results = $dbProvider->setQuery($sql)
							  ->execute(array("parentid"		=>	$parentDocument->getUniqueId(),
											  "childid"			=>	$childDocument->getUniqueId(),
											  "relationType"	=>	$relationType,
							  				  "lang"			=>	$lang))
							  ->getQueryResults(PDO::FETCH_COLUMN);

		return $results[0];
    }
		
	/**
	 * Check if a relation already exist
	 *
	 * @param f_document_BaseDocument $parentDocument
	 * @param f_document_BaseDocument $childDocument
	 * @param string $relationType
	 * @param integer $orderNumber
	 * @return boolean
	 */
	public function checkIfRelationExists($parentDocument, $childDocument, $relationType, $lang = null)
	{
		$dbProvider = f_database_Provider::getInstance();
		
		$sql = "SELECT relation_type FROM f_relation WHERE parent_id=:parentid AND child_id=:childid AND relation_type=:relationType AND lang=:lang";			
		
		$results = array();
		
		$results = $dbProvider->setQuery($sql)
							  ->execute(array("parentid"		=>	$parentDocument->getUniqueId(), 
											  "childid"			=>	$childDocument->getUniqueId(),
											  "relationType"	=>	$relationType,
							  				  "lang"			=>	$lang))
							  ->getQueryResults(PDO::FETCH_COLUMN);
		
		return isset($results[0]); 
	}
	
	/**
	 * Delete a relation
	 *
	 * @param f_document_BaseDocument $parentDocument
	 * @param f_document_BaseDocument $childDocument
	 * @param string $relationType
	 * @param string $lang
	 */
	public function clearRelation($parentDocument, $childDocument, $relationType, $lang)
	{
		$dbProvider = f_database_Provider::getInstance();	
		$cache = f_core_Cache::getInstance();

		$parentsIds = $this->getAllParentsId($childDocument->getUniqueId(), $relationType, $lang);
		
		$sql = "DELETE FROM f_relation WHERE parent_id=:parentid AND child_id=:childid AND relation_type=:relationType AND lang=:lang";			
		
		$results = array();
		
		$results = $dbProvider->setQuery($sql)
							  ->execute(array("parentid"		=>	$parentDocument->getUniqueId(), 
							  				  "childid"			=>	$childDocument->getUniqueId(),											
											  "relationType"	=>	$relationType,
							  				  "lang"			=>	$lang));

		if(count($parentsIds) >= 1)
		{
			foreach($parentsIds as $parentId)
			{
				$cache->clearCache($cache->getDocumentCacheKey($parentId, $lang));	
			}
		}
	}
	
	/**
	 * Remove all relations of all kind for a given document
	 * 
	 * @param $document
	 * @param $lang
	 */
	public function clearAllRelations($document, $lang)
	{
		$dbProvider = f_database_Provider::getInstance();	
		$cache = f_core_Cache::getInstance();

		if(is_null($lang))
		{
			$context = f_core_Context::getInstance();
			$lang = $context->getLang();
		}
		
		$sql = "SELECT `parent_id` FROM `f_relation` WHERE `child_id`=:childId AND `lang`=:lang";
		
		$parentsIds = $dbProvider->setQuery($sql)->execute(array("childId" => $document->getUniqueId(), "lang" => $lang))->getQueryResults(PDO::FETCH_COLUMN);
		
		$sql = "DELETE FROM f_relation WHERE parent_id=:documentId OR child_id=:documentId AND lang=:lang";
		
		$results = array();
		
		$results = $dbProvider->setQuery($sql)
							  ->execute(array("documentId"		=>	$document->getUniqueId(),
							  				  "lang"			=>	$lang));

		if(count($parentsIds) >= 1)
		{
			foreach($parentsIds as $parentId)
			{
				$cache->clearCache($cache->getDocumentCacheKey($parentId, $lang));	
			}
		}		
	}
	
	/**
	 * Remove parents cache
	 * 
	 * @param integer $childId
	 * @param string $relationType
	 * @param integer $lang
	 * @return nb of affected lines
	 */
	public function regenerateParentCache($childId, $relationType, $lang = null)
	{
		if(is_null($lang))
		{
			$lang = f_core_Context::getInstance()->getLang();
		}
				
		$parents = $this->getAllParents($childId, $relationType, $lang);
		
		$count = 0;
		
		if(count($parents) >= 1)
		{
			$cache = f_core_Cache::getInstance();
			
			foreach ($parents as $parent)
			{
				$cache->clearCache($cache->getDocumentCacheKey($parent->getUniqueId(), $lang));
				$count++;
			}
		}
		
		return $count;
	}
	
	/**
	 * Order or re-order relations for a given document
	 * 
	 * @example orderRelations(25, "FR", array(0 => 23, 1 => 28, 2 => 33))
	 * @param integer $parentId
	 * @param string $relationLang
	 * @param array $orderingArray
	 * @return f_relation_Manager
	 */
	public function orderRelations($parentId, $relationLang, $orderingArray)
	{
		$dbProvider = f_database_Provider::getInstance();
		
		$dbProvider->beginTransaction();
		foreach($orderingArray as $orderNb => $childId)
		{
			$dbProvider->updateByKey("f_relation", array("parent_id" => $parentId, "child_id" => $childId, "lang" => strtoupper($relationLang)), array("relation_order" => $orderNb));			
		}
		$dbProvider->commit();
		
		return $this;
	}
	
// ======================================== GETTERS	
	
	/**
	 * Get parent (!! USE ONLY WHEN PARENT IS UNIQUE !!)
	 * @param $childId
	 * @param $lang
	 * @return f_document_BaseDocument
	 */
	public function getUniqueParent($childId, $lang = null)
	{
		$dbProvider = f_database_Provider::getInstance();
		
		if(is_null($lang)) $lang = f_core_Context::getInstance()->getLang();
		
		$sql = "SELECT `parent_id`, `parent_model` FROM `f_relation` WHERE `child_id`=:childId AND `lang`=:lang LIMIT 1";
		
		$parent = $dbProvider->setQuery($sql)->execute(array("childId" => $childId, "lang" => strtoupper($lang)))->getQueryResult(PDO::FETCH_ASSOC);

		if(!is_null($parent) && is_array($parent) && !empty($parent))
		{
			return f_document_Provider::getInstance()->getByModelAndUniqueId($parent['parent_model'], $parent['parent_id'], $lang);
		}
		
		return null;
	}
	
	/**
	 * Return the quantity of childs
	 *
	 * @param object $parentDocument
	 * @param string or array $relationType
	 * @param string $lang
	 * @return unknown
	 */
	public function getChildsNb($parentDocument, $relationType, $lang)
	{
		$dbProvider = f_database_Provider::getInstance();

		if(!is_array($relationType))
		{
			$var = $relationType;
			$relationType = array();
			$relationType[] = $var;
		}

        $relationType = implode("','", $relationType);

		$sql = "SELECT COUNT(`child_id`) FROM `f_relation` WHERE `relation_type` IN ('$relationType') AND `lang`=:lang AND `parent_id`=:parentId";
		
		$nbRelations = $dbProvider->setQuery($sql)->execute(array("lang" => $lang, "parentId" => $parentDocument->getUniqueId()))->getQueryResult(PDO::FETCH_COLUMN);

		return intval($nbRelations);
	}

	/**
	 * Return the quantity of childs
	 *
	 * @param object $parentDocument
	 * @param string or array $documentTypes
	 * @param string $lang
	 * @return unknown
	 */
	public function getChildsNbByDocType($parentDocument, $documentTypes, $lang = '*')
	{
		$dbProvider = f_database_Provider::getInstance();

		if(!is_array($documentTypes))
		{
			$var = $documentTypes;
			$documentTypes = array();
			$documentTypes[] = $var;
		}

        $documentTypes = implode("','", $documentTypes);

		$sql = "SELECT COUNT(`child_id`) FROM `f_relation` WHERE `child_model` IN ('$documentTypes') AND `parent_id`=:parentId";
        $args = array("parentId" => $parentDocument->getUniqueId());

		if($lang != '*')
		{
			$args['lang'] = $lang;
			$sql .= " AND `lang`=:lang";
		}

		$nbRelations = $dbProvider->setQuery($sql)->execute($args)->getQueryResult(PDO::FETCH_COLUMN);

		return intval($nbRelations);
	}
	
	/**
	 * Get child(s) document(s) from given relation type
	 *
	 * @param integer $parentId
	 * @param string $lang
	 * @param string $relationType
	 * @return array
	 */
	public function getChildsById($parentId, $lang, $relationType)
	{
		$parentDocument = f_document_Provider::getInstance()->getByUniqueId($parentId, $lang);
		return $this->getChilds($parentDocument, $relationType, $lang);
	}

    public function getChildsByIdRecursive($parentId, $lang, $relationType, $result = array())
    {
        $childs = $this->getChildsById($parentId, $lang, $relationType);

        if(is_array($childs) && count($childs) > 0)
		{
			foreach($childs as $child)
			{
				$result[] = $child;
				$result = $this->getChildsByIdRecursive($child->getUniqueId(), $lang, $relationType, $result);
            }
		}

        return $result;
    }
	
	/**
	 * Get child(s) document(s) from given relation type
	 *
	 * @param f_document_BaseDocument $parentDocument
	 * @param string $relationType
	 * @param string $lang
	 * @return array <document>
	 */
	public function getChilds($parentDocument, $relationType, $lang, $nbResult = null, $offset = null, $order = null)
	{
        if(is_null($order)) $order = array("relation_order" => "ASC");
        
		$childIds = $this->getChildIds($parentDocument->getUniqueId(), $relationType, $lang, $nbResult, $offset, $order);

		$results = array();
		$resultsOrdered = array();
		$resultsNotOrdered = array();

		if(is_array($childIds) && isset($childIds[0]))
		{
			$documentProvider = f_document_Provider::getInstance();
			
			foreach ($childIds as $childId)
			{
				if($childId['relation_order'] > 0)
				{
                    $resultsOrdered[] = $documentProvider->getByModelAndUniqueId($childId['child_model'], $childId['child_id'], $lang);
				} 
				else
				{
					$resultsNotOrdered[] = $documentProvider->getByModelAndUniqueId($childId['child_model'], $childId['child_id'], $lang);
				}	
			}

			$results = array_merge($resultsOrdered, $resultsNotOrdered);            
		}
		
		return $results;
	}
	
	/**
	 * Get child(s) id(s) from relation type
	 *
	 * @param integer $parentId
	 * @param string $relationType
	 * @return array <integer> document_uid
	 */
	public function getChildIds($parentId, $relationType, $lang = null, $nbResult = null, $offset = null, $order = array("relation_order" => "ASC"))
	{
		if(is_null($lang))
		{
			$lang = f_core_Context::getInstance()->getLang();
		}
		
		$dbProvider = f_database_Provider::getInstance();
        $params = array(":parentid"=>$parentId, ":lang"=>$lang);
        $typeQuery = "";

        if($relationType != "*")
        {
            $params[":relationType"] = $relationType;
            $typeQuery = "`relation_type`=:relationType AND";
        }

		$sql = "SELECT `child_id`, `relation_order`, `child_model` FROM `f_relation` WHERE `parent_id`=:parentid AND $typeQuery `lang`=:lang";

		if(!is_null($order) && is_array($order))
		{
			$sqlStr = " ORDER BY ";
			$sqlOrder = array();

			foreach($order as $k => $v)
			{
				$sqlOrder[] = "`".$k."` ".$v;
			}

			$sqlStr .= join(", ", $sqlOrder);
            $sql .= $sqlStr;
		}

		if(!is_null($nbResult))
		{
			$sql .= " LIMIT ";
			
			if(!is_null($offset))
			{
				$sql .= $offset.",";
			}
			
			$sql .= $nbResult;
		}
        
		return $dbProvider	->setQuery($sql)
							->execute($params)
							->getQueryResults(PDO::FETCH_ASSOC);
	}
	
	/**
	 * Get child(s) id(s) from given document(s) type(s)
	 *
	 * @param integer $parentId
	 * @param string or array $documentTypes
	 * @param string $lang
	 * @return array
	 */
	public function getChildsIdsByDocType($parentId, $documentTypes, $lang = null, $order = null, $nbResult = null, $offset = null)
	{
		if(is_null($lang))
		{
			$lang = f_core_Context::getInstance()->getLang();
		}

		if(!is_array($documentTypes))
		{
			$var = $documentTypes;
			$documentTypes = array();
			$documentTypes[] = $var;
		}

		$documentTypes = join("','", $documentTypes);

		$dbProvider = f_database_Provider::getInstance();
		
		$sqlStr = ""; $args = array();
		
		if(!is_null($order) && is_array($order))
		{
			$sqlStr = " ORDER BY ";
			$sqlOrder = array();
			
			foreach($order as $k => $v)
			{
				$sqlOrder[] = "`".$k."` ".$v;
			}
			
			$sqlStr .= join(", ", $sqlOrder);
		}

        if(!is_null($nbResult))
		{
			$sqlStr .= " LIMIT ";

			if(!is_null($offset))
			{
				$sqlStr .= $offset.",";
			}

			$sqlStr .= $nbResult;
		}

		$sql = "SELECT `child_id`, `child_model`, `lang` FROM `f_relation` WHERE `parent_id`=:parentId AND `child_model` IN ('$documentTypes')";
		
		$args['parentId'] = $parentId;
		
		if($lang != "*")
		{
			$args['lang'] = $lang;
			$sql .= " AND `lang`=:lang";
		}

		return $dbProvider->setQuery($sql.$sqlStr)->execute($args)->getQueryResults(PDO::FETCH_ASSOC);
	}
	
	/**
	 * Get all childs from the given parent id by document type(s)
	 * 
	 * @param $parentId
	 * @param $documentTypes
	 * @param $lang
	 * @return unknown_type
	 */
	public function getChildsByDocType($parentId, $documentTypes, $lang = null, $relationOrder = null, $publicationStatus = null, $nbResult = null, $offset = null)
	{
		$documentIds = $this->getChildsIdsByDocType($parentId, $documentTypes, $lang, $relationOrder, $nbResult, $offset);

		$documentProvider = f_document_Provider::getInstance();		
		
		$result = array();

		if($lang != "*")
		{
			foreach($documentIds as $documentId)
			{
				if(!is_null($publicationStatus))
				{
					$document = $documentProvider->getByModelAndUniqueId($documentId['child_model'], $documentId['child_id'], $lang);
					
					if($document->getPublicationStatus() == $publicationStatus)
					{
						$result[] = $document;
					}
				}
				else
				{
					$result[] = $documentProvider->getByModelAndUniqueId($documentId['child_model'], $documentId['child_id'], $lang);
				}
			}			
		}
		else
		{
			foreach($documentIds as $documentId)
			{
				if(!is_null($publicationStatus))
				{
					$document = $documentProvider->getByModelAndUniqueId($documentId['child_model'], $documentId['child_id'], $documentId["lang"]);
					
					if($document->getPublicationStatus() == $publicationStatus)
					{
						$result[] = $document;
					}
				}
				else
				{
					$result[] = $documentProvider->getByModelAndUniqueId($documentId['child_model'], $documentId['child_id'], $documentId["lang"]);
				}
			}
		}		
		
		return $result;
	}
	
	/**
	 * Get childs ids recursively
	 * @param integer $parentId
	 * @param array $documentTypes
	 * @param string $lang
	 * @param array $order
	 * @return array
	 */
	public function getChildsIdsByDocTypeRecursive($parentId, $documentTypes, $lang = null, $order = null, $result = array())
	{
		$childs = $this->getChildsIdsByDocType($parentId, $documentTypes, $lang, $order);

		if(is_array($childs) && count($childs) > 0)
		{
			foreach($childs as $child)
			{		
				$result[] = $child;
				$result = $this->getChildsIdsByDocTypeRecursive($child['child_id'], $documentTypes, $lang, $order, $result);			
            }
		}
        
        //return array_unique($result);
		return $result;
	}
	
	/**
	 * Get childs recursively
	 * @param integer $parentId
	 * @param array $documentTypes
	 * @param string $lang
	 * @param array $order
	 * @return array
	 */
	public function getChildsByDocTypeRecursive($parentId, $documentTypes, $lang = null, $order = null, $publicationStatus = null)
	{
		$childIds = $this->getChildsIdsByDocTypeRecursive($parentId, $documentTypes, $lang, $order);
		$docProvider = f_document_Provider::getInstance();
        $result = array();

		foreach($childIds as $childId)
		{
            if(!is_null($publicationStatus))
            {
                $document = $docProvider->getByModelAndUniqueId($childId['child_model'], $childId['child_id'], $childId['lang']);

                if($document->getPublicationStatus() == $publicationStatus)
                {
                    $result[] = $document;
                }
            }
            else
            {
                $result[] = $docProvider->getByModelAndUniqueId($childId['child_model'], $childId['child_id'], $childId['lang']);
            }
		}

		return $result;
	}
	
	/**
	 * Get parent document from a child id
	 *
	 * @param integer $childId
	 * @param string $relationType
	 * @return array <document>
	 */
	public function getParent($childId, $relationType, $lang=null)
	{
		$parentId = $this->getParentId($childId, $relationType, $lang);
		
		if(!is_null($parentId))
		{
			$documentProvider = f_document_Provider::getInstance();
			
			return $documentProvider->getByModelAndUniqueId($parentId['parent_model'], $parentId['parent_id'], $lang);			
		}
		
		return null;
	}
	
	/**
	 * Get parent document id from a child id
	 *
	 * @param integer $childId
	 * @param string $relationType
	 * @return array <integer> document_uid
	 */
	public function getParentId($childId, $relationType, $lang=null)
	{
		$dbProvider = f_database_Provider::getInstance();
		
		if(is_null($lang))
		{
			$context = f_core_Context::getInstance();
			$lang = $context->getLang();
		}
		
		$sql = "SELECT `parent_id`, `parent_model` FROM `f_relation` WHERE `child_id`=:childId AND `relation_type`=:relationType AND `lang`=:lang LIMIT 1";
		
		return $dbProvider->setQuery($sql)->execute(array("childId" => $childId, "relationType" => $relationType, "lang" => $lang))->getQueryResult(PDO::FETCH_ASSOC);
	}
	
	/**
	 * Get all parent documents from a child id
	 *
	 * @param integer $childId
	 * @param string $relationType
	 * @return array <document>
	 */
	public function getAllParents($childId, $relationType, $lang = null)
	{
		$parentIds = $this->getAllParentsId($childId, $relationType, $lang);
		
		if(!is_null($parentIds))
		{
			$documentProvider = f_document_Provider::getInstance();
			foreach($parentIds as $parentId)
			{					
				$result[] = $documentProvider->getByModelAndUniqueId($parentId['parent_model'], $parentId['parent_id'], $lang);							
			}
		}
		
		if(isset($result[0]))
		{
			return $result;
		}
		
		return null;
	}	
	
	/**
	 * Get all parent documents ids from a child id
	 *
	 * @param integer $childId
	 * @param string $relationType
	 * @return array <integer> document_uid
	 */
	public function getAllParentsId($childId, $relationType, $lang = null)
	{
		$dbProvider = f_database_Provider::getInstance();
		
		if(is_null($lang))
		{
			$context = f_core_Context::getInstance();
			$lang = $context->getLang();
		}
		
		$sql = "SELECT `parent_id`,`parent_model` FROM `f_relation` WHERE `child_id`=:childId AND `relation_type`=:relationType AND `lang`=:lang";
		
		return $dbProvider->setQuery($sql)->execute(array("childId" => $childId, "relationType" => $relationType, "lang" => $lang))->getQueryResults(PDO::FETCH_ASSOC);
	}

    /**
	 * Get all parent documents ids from a child id
	 *
	 * @param integer $childId
	 * @param string $relationType
	 * @return array <integer> document_uid
	 */
	public function getAllChildsId($parentId, $lang = null, $relationType = null)
	{
		$dbProvider = f_database_Provider::getInstance();

		if(is_null($lang))
		{
			$context = f_core_Context::getInstance();
			$lang = $context->getLang();
		}

        if(!is_null($relation))
        {
            $sql = "SELECT `child_id`,`child_model` FROM `f_relation` WHERE `parent_id`=:parent_id AND `relation_type`=:relationType AND `lang`=:lang";
            return $dbProvider->setQuery($sql)->execute(array("parent_id" => $parentId, "relationType" => $relationType, "lang" => $lang))->getQueryResults(PDO::FETCH_ASSOC);
        }
        else
        {
            $sql = "SELECT `child_id`,`child_model` FROM `f_relation` WHERE `parent_id`=:parent_id AND `lang`=:lang";
            return $dbProvider->setQuery($sql)->execute(array("parent_id" => $parentId, "lang" => $lang))->getQueryResults(PDO::FETCH_ASSOC);
        }
	}
	
	/**
	 * Get parent document from the given child id and document type
	 * 
	 * @param $childId
	 * @param $docType
	 * @param $lang
	 * @return unknown_type
	 */
	public function getParentByDocType($childId, $docType, $lang = null)
	{
		$parentId = $this->getParentIdByDocType($childId, $docType, $lang);

		if(!is_null($parentId))
		{
			$documentProvider = f_document_Provider::getInstance();
			
			return $documentProvider->getByUniqueId($parentId, $lang);
		}
		
		return null;
	}
	
	/**
	 * Get parent id from the given child id and document type
	 * 
	 * @param $childId
	 * @param $docType
	 * @param $lang
	 * @return unknown_type
	 */
	public function getParentIdByDocType($childId, $docType, $lang = null)
	{
		$dbProvider = f_database_Provider::getInstance();
		
		if(is_null($lang))
		{
			$context = f_core_Context::getInstance();
			$lang = $context->getLang();
		}
		
		$sql = "SELECT `parent_id` FROM `f_relation` WHERE `child_id`=:childId AND `parent_model`=:docType AND `lang`=:lang LIMIT 1";
		
		return $dbProvider->setQuery($sql)->execute(array('childId' => $childId, "docType" => $docType, "lang" => $lang))->getQueryResult(PDO::FETCH_COLUMN);
	}

	/**
	 * Get all parents from the given child by relation Type
	 *
	 * @param integer $childId
	 * @param string $relationType
	 * @param string $lang
	 * @param array $result
	 * @return array
	 */
	public function getParents($childId, $relationType, $lang=null, $result = null)
	{	
		$parent = $this->getParent($childId, $relationType, $lang);

		if(!is_null($parent))
		{
			$result[] = $parent;
			return $this->getParents($parent->getUniqueId(), $relationType, $lang, $result);
		}
		else
		{
			if(is_array($result))
			{
				rsort($result);
			}	
			return $result;			
		}	
	}
	
	/**
	 * Get all parents documents recursively by document type(s)
	 * 
	 * @param integer $childId
	 * @param arr|string $documentTypes
	 * @param string $lang
	 * @return array
	 */
	public function getParentsByDocType($childId, $documentTypes, $lang = null, $order = null)
	{		
		$documentIds = $this->getParentsIdsByDocType($childId, $documentTypes, $lang, $order);

		$documentProvider = f_document_Provider::getInstance();		
		
		$result = array();

		if(is_array($documentIds) && count($documentIds) > 0)
		{
			sort($documentIds);
			foreach($documentIds as $documentId)
			{
				$result[] = $documentProvider->getByModelAndUniqueId($documentId['parent_model'], $documentId['parent_id'], $lang);
			}
		}

		return $result;
	}
	
	/**
	 * Get all parents ids recursively by document type(s)
	 * 
	 * @param integer $childId
	 * @param array|string $documentTypes
	 * @param string $lang
	 * @param array $result
	 * @return array|null
	 */
	public function getParentsIdsByDocType($childId, $documentTypes, $lang = null, $order = null, $result = null, $onlyId = false)
	{
		if(is_null($lang))
		{
			$context = f_core_Context::getInstance();
			$lang = $context->getLang();
		}

		if(!is_array($documentTypes))
		{
			$documentTypesArray[] = $documentTypes;
			$documentTypes = $documentTypesArray;
		}

		$documentTypesString = join("','", $documentTypes);
		
		$sqlStr = "";
		
		if(!is_null($order) && is_array($order))
		{
			$sqlStr = " ORDER BY ";
			$sqlOrder = array();
			
			foreach($order as $k => $v)
			{
				$sqlOrder[] = "`".$k."` ".$v;
			}
			
			$sqlStr .= join(", ", $sqlOrder);
		}		

		$dbProvider = f_database_Provider::getInstance();
		
		if($lang != "*")
		{
			$sql = "SELECT `parent_id`,`parent_model` FROM `f_relation` WHERE `child_id`=:childId AND `lang`=:lang ".$sqlStr;
			$parents = $dbProvider->setQuery($sql)->execute(array('childId' => $childId, "lang" => $lang))->getQueryResults(PDO::FETCH_ASSOC);
		}
		else
		{
			$sql = "SELECT `parent_id`,`parent_model` FROM `f_relation` WHERE `child_id`=:childId ".$sqlStr;
			$parents = $dbProvider->setQuery($sql)->execute(array('childId' => $childId))->getQueryResults(PDO::FETCH_ASSOC);			
		}

		if(is_array($parents) && count($parents) > 0)
		{
			foreach($parents as $parent)
			{
				if(in_array($parent["parent_model"], $documentTypes))
				{
                    if($onlyId)
                    {
                        $result[] = $parent['parent_id'];
                    }
                    else
                    {
                        $result[] = $parent;
                    }
				}

				return $this->getParentsIdsByDocType($parent["parent_id"], $documentTypes, $lang, $order, $result, $onlyId);
			}			
		}
		else
		{
			if(is_array($result))
			{
				return array_reverse($result);
			}
			return $result;
		}
	}
}